home *** CD-ROM | disk | FTP | other *** search
- /*
-
- AJR
-
- I have found many example of GIF decoders, but this is the only one
- that worked. Steve Rimmer's otherwise excelent book (Supercharged
- Bitmapped Graphics) has a giff decoder, but it didn't work
- (for me anyways - maybe I made a typeo :( ).
-
- Anyways here is code, by Steven A. Bennet that works.
- Now remember Unisys is now charging royalties for all
- commercial use of the gif format!!!!
-
- This file has two file, a C file, followed by an H file.
- the C file has a bit of code specific to the project I was using
- the code for, but it should be easy to remove/replace with your
- own routines.
-
-
- tested on 640x400 256 colour images.
-
- */
-
-
- /*
-
- giff.c
-
- unpackgiffblock()Copyright (C) 1987, by Steven A. Bennett
-
- Modified by AJR june 1994 to work with personal lib.
- Note! this code contains references to stuff in AJR's
- private libs, but all of this stuff is easily replaced with
- your own code.
-
- memalloc() - replace with farmalloc()
- memfree() - replace with farfree()
- pr2() - debug code, print to second monitor
- g_get_byte() - gets the next byte from the file,
- if you want SLOW code just use fread()
- otherwise write some buffering code.
- g_read_bytes() - next x bytes, see above comment
- g_skip_bytes() - skip next x bytes (fseek() sortof)
- other stuff noted in the code.
-
- All comments by Steven, unless otherwise noted.
-
- AJR
- Created - 1994//6//22
-
- History:
- New file
-
- */
-
- // AJR not ALL of these include are required, I am just lazy
- // and include everything.
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <alloc.h>
- #include <dos.h>
- #include <string.h>
- #include <time.h>
- #include <conio.h>
- #include <ctype.h>
- #include <dir.h>
- #include <bios.h>
- #include <process.h>
- #include <io.h>
-
-
- #include "giff.h"
-
- // AJR bunch of project specific includes snippped here
-
-
- BITMAP far *giff_shape; // AJR used by my draw code
- short giff_x, giff_y; // AJR ditto
-
- // AJR - code to skip weird blocks
- /* ---------------------- skip_ext() ---------------------- June 22,1994 */
- short skip_ext(g_file_t *gf)
- {
- plaintext_t pt;
- controlblock_t cb;
- application_t ap;
- unsigned char c, n;
-
- c=g_get_byte(gf);
- switch ( c )
- {
- case 0x01: // plain text descriptor
- //pr2("got plain text block");
- g_read_bytes((char far *)&pt, sizeof(pt), gf);
- n=g_get_byte(gf);
- // skip text
- g_skip_bytes(n, gf);
- break;
-
- case 0xf9: // graphic control block
- //pr2("control block");
- //pr2("sizeof cb = %d (6)", sizeof(cb));
- g_read_bytes((char far *)&cb, sizeof(cb), gf);
- break;
-
- case 0xfe: // comment block
- //pr2("comment block");
- n=g_get_byte(gf);
- // skip comment
- g_skip_bytes(n, gf);
- break;
-
- case 0xff: // application ext
- //pr2("application block");
- g_read_bytes((char far *)&ap, sizeof(cb), gf);
- n=g_get_byte(gf);
- // skip data
- g_skip_bytes(n, gf);
- break;
-
- default:
- //pr2("unknown extension %d skipped", c);
- n=g_get_byte(gf);
- // skip data
- g_skip_bytes(n, gf);
- break;
- }
-
- return(FALSE);
- }
-
-
- /* DECODE.C - An LZW decoder for GIF
- * Copyright (C) 1987, by Steven A. Bennett
- *
- * Permission is given by the author to freely redistribute and include
- * this code in any program as long as this credit is given where due.
- *
- * In accordance with the above, I want to credit Steve Wilhite who wrote
- * the code which this is heavily inspired by...
- *
- * GIF and 'Graphics Interchange Format' are trademarks (tm) of
- * Compuserve, Incorporated, an H&R Block Company.
- *
- * Release Notes: This file contains a decoder routine for GIF images
- * which is similar, structurally, to the original routine by Steve Wilhite.
- * It is, however, somewhat noticably faster in most cases.
- *
- */
-
-
- #if 0
- /* short get_byte()
- *
- * - This external (machine specific) function is expected to return
- * either the next byte from the GIF file, or a negative number, as
- * defined in ERRS.H.
- */
- short get_byte();
-
- /* short out_line(pixels, linelen)
- * UBYTE pixels[];
- * short linelen;
- *
- * - This function takes a full line of pixels (one byte per pixel) and
- * displays them (or does whatever your program wants with them...). It
- * should return zero, or negative if an error or some other event occurs
- * which would require aborting the decode process... Note that the length
- * passed will almost always be equal to the line length passed to the
- * decoder function, with the sole exception occurring when an ending code
- * occurs in an odd place in the GIF file... In any case, linelen will be
- * equal to the number of pixels passed...
- */
- short out_line();
-
- /* short bad_code_count;
- *
- * This value is the only other global required by the using program, and
- * is incremented each time an out of range code is read by the decoder.
- * When this value is non-zero after a decode, your GIF file is probably
- * corrupt in some way...
- */
- #endif
-
-
- // AJR - this draws one complete decoded line of graphic
- // you can alter this routine to draw, store whatever you like.
- /* ---------------------- out_line() ---------------------- June 27,1994 */
- short out_line(unsigned char far *pixels, short width)
- {
- _fmemcpy(&giff_shape->data,pixels, width);
- drawshape(giff_x, giff_y, giff_shape);
- giff_y++;
-
- return 0;
- }
-
-
- short bad_code_count;
-
- #define MAX_CODES 4095
-
- /* Static variables */
- static short curr_size; /* The current code size */
- static short clear; /* Value for a clear code */
- static short ending; /* Value for a ending code */
- static short newcodes; /* First available code */
- static short top_slot; /* Highest code for current size */
- static short slot; /* Last read code */
-
- /* The following static variables are used
- * for seperating out codes
- */
- static short navail_bytes = 0; /* # bytes left in block */
- static short nbits_left = 0; /* # bits left in current byte */
- static unsigned char b1; /* Current byte */
- static unsigned char byte_buff[257]; /* Current block */
- static unsigned char *pbytes; /* Pointer to next byte in block */
-
- static LONG code_mask[13] = {
- 0,
- 0x0001, 0x0003,
- 0x0007, 0x000F,
- 0x001F, 0x003F,
- 0x007F, 0x00FF,
- 0x01FF, 0x03FF,
- 0x07FF, 0x0FFF
- };
-
- /* get_next_code()
- * - gets the next code from the GIF file. Returns the code, or else
- * a negative number in case of file errors...
- */
- static short get_next_code(g_file_t *gf)
- {
- short i, x;
- unsigned long ret;
-
- if (nbits_left == 0)
- {
- if (navail_bytes <= 0)
- {
-
- /* Out of bytes in current block, so read next block
- */
- pbytes = byte_buff;
- if ((navail_bytes = g_get_byte(gf)) < 0)
- return(navail_bytes);
- else if (navail_bytes)
- {
- for (i = 0; i < navail_bytes; ++i)
- {
- if ((x = g_get_byte(gf)) < 0)
- return(x);
- byte_buff[i] = x;
- }
- }
- }
- b1 = *pbytes++;
- nbits_left = 8;
- --navail_bytes;
- }
-
- ret = b1 >> (8 - nbits_left);
- while (curr_size > nbits_left)
- {
- if (navail_bytes <= 0)
- {
-
- /* Out of bytes in current block, so read next block
- */
- pbytes = byte_buff;
- if ((navail_bytes = g_get_byte(gf)) < 0)
- return(navail_bytes);
- else if (navail_bytes)
- {
- for (i = 0; i < navail_bytes; ++i)
- {
- if ((x = g_get_byte(gf)) < 0)
- return(x);
- byte_buff[i] = x;
- }
- }
- }
- b1 = *pbytes++;
- ret |= b1 << nbits_left;
- nbits_left += 8;
- --navail_bytes;
- }
- nbits_left -= curr_size;
- ret &= code_mask[curr_size];
- return((short)(ret));
- }
-
-
- /* The reason we have these seperated like this instead of using
- * a structure like the original Wilhite code did, is because this
- * stuff generally produces significantly faster code when compiled...
- * This code is full of similar speedups... (For a good book on writing
- * C for speed or for space optomisation, see Efficient C by Tom Plum,
- * published by Plum-Hall Associates...)
- */
-
- static unsigned char far *stack; /* Stack for storing pixels */
- static unsigned char far *suffix; /* Suffix table */
- static unsigned short far *prefix; /* Prefix linked list */
-
- /* short decoder(linewidth)
- * short linewidth; * Pixels per line of image *
- *
- * - This function decodes an LZW image, according to the method used
- * in the GIF spec. Every *linewidth* "characters" (ie. pixels) decoded
- * will generate a call to out_line(), which is a user specific function
- * to display a line of pixels. The function gets it's codes from
- * get_next_code() which is responsible for reading blocks of data and
- * seperating them into the proper size codes. Finally, get_byte() is
- * the global routine to read the next byte from the GIF file.
- *
- * It is generally a good idea to have linewidth correspond to the actual
- * width of a line (as specified in the Image header) to make your own
- * code a bit simpler, but it isn't absolutely necessary.
- *
- * Returns: 0 if successful, else negative. (See ERRS.H)
- *
- */
-
- short unpackgiffblock(short linewidth, g_file_t *gf, short dx, short dy)
- {
- unsigned char *bufptr;
- unsigned char far *sp;
- unsigned char *buf;
- short code, fc, oc, bufcnt;
- short c, size, ret;
-
- giff_shape=allocwindow(linewidth, 1,1,1,0); // AJR - init draw code
- if ( giff_shape )
- {
- giff_x=dx;
- giff_y=dy;
-
- /* Initialize for decoding a new image...
- */
- size=g_get_byte(gf);
- if (size < 2 || 9 < size )
- return(BAD_CODE_SIZE);
-
-
- stack=memalloc(MAX_CODES +1); /* Stack for storing pixels */
- suffix=memalloc(MAX_CODES + 1); /* Suffix table */
- prefix=memalloc((MAX_CODES + 1)*sizeof(short)); /* Prefix linked list */
-
- if ( prefix == NULL )
- {
- if ( suffix )
- memfree(suffix);
- if ( stack )
- memfree(stack);
- pr2("out of mem");
- return OUT_OF_MEMORY;
- }
-
- curr_size = size + 1;
- top_slot = 1 << curr_size;
- clear = 1 << size;
- ending = clear + 1;
- slot = newcodes = ending + 1;
- navail_bytes = nbits_left = 0;
- bad_code_count=0;
-
- /* Initialize in case they forgot to put in a clear code.
- * (This shouldn't happen, but we'll try and decode it anyway...)
- */
- oc = fc = 0;
-
- /* Allocate space for the decode buffer
- */
- if ((buf = (unsigned char *)malloc(linewidth + 1)) == NULL)
- {
- pr2("out of mem 2");
- return(OUT_OF_MEMORY);
- }
-
- /* Set up the stack pointer and decode buffer pointer
- */
- sp = stack;
- bufptr = buf;
- bufcnt = linewidth;
-
- /* This is the main loop. For each code we get we pass through the
- * linked list of prefix codes, pushing the corresponding "character" for
- * each code onto the stack. When the list reaches a single "character"
- * we push that on the stack too, and then start unstacking each
- * character for output in the correct order. Special handling is
- * included for the clear code, and the whole thing ends when we get
- * an ending code.
- */
- while ((c = get_next_code(gf)) != ending )
- {
-
- /* If we had a file error, return without completing the decode
- */
- if (c < 0)
- {
- free(buf);
- return(0);
- }
-
- /* If the code is a clear code, reinitialize all necessary items.
- */
- if ( c == clear )
- {
- curr_size = size + 1;
- slot = newcodes;
- top_slot = 1 << curr_size;
-
- /* Continue reading codes until we get a non-clear code
- * (Another unlikely, but possible case...)
- */
- while ((c = get_next_code(gf)) == clear)
- ;
-
- /* If we get an ending code immediately after a clear code
- * (Yet another unlikely case), then break out of the loop.
- */
- if (c == ending)
- break;
-
- /* Finally, if the code is beyond the range of already set codes,
- * (This one had better NOT happen... I have no idea what will
- * result from this, but I doubt it will look good...) then set it
- * to color zero.
- */
- if (c >= slot)
- c = 0;
-
- oc = fc = c;
-
- /* And let us not forget to put the char into the buffer... And
- * if, on the off chance, we were exactly one pixel from the end
- * of the line, we have to send the buffer to the out_line()
- * routine...
- */
- *bufptr++ = c;
- if (--bufcnt == 0)
- {
- if ((ret = out_line((char far *)buf, linewidth)) < 0)
- {
- free(buf);
- return(ret);
- }
- bufptr = buf;
- bufcnt = linewidth;
- }
- }
- else
- {
-
- /* In this case, it's not a clear code or an ending code, so
- * it must be a code code... So we can now decode the code into
- * a stack of character codes. (Clear as mud, right?)
- */
- code = c;
-
- /* Here we go again with one of those off chances... If, on the
- * off chance, the code we got is beyond the range of those already
- * set up (Another thing which had better NOT happen...) we trick
- * the decoder into thinking it actually got the last code read.
- * (Hmmn... I'm not sure why this works... But it does...)
- */
- if (code >= slot)
- {
- if (code > slot)
- ++bad_code_count;
- code = oc;
- *sp++ = fc;
- }
-
- /* Here we scan back along the linked list of prefixes, pushing
- * helpless characters (ie. suffixes) onto the stack as we do so.
- */
- while (code >= newcodes)
- {
- *sp++ = suffix[code];
- code = prefix[code];
- }
-
- /* Push the last character on the stack, and set up the new
- * prefix and suffix, and if the required slot number is greater
- * than that allowed by the current bit size, increase the bit
- * size. (NOTE - If we are all full, we *don't* save the new
- * suffix and prefix... I'm not certain if this is correct...
- * it might be more proper to overwrite the last code...
- */
- *sp++ = code;
- if (slot < top_slot)
- {
- suffix[slot] = fc = code;
- prefix[slot++] = oc;
- oc = c;
- }
- if (slot >= top_slot)
- if (curr_size < 12)
- {
- top_slot <<= 1;
- ++curr_size;
- }
-
- /* Now that we've pushed the decoded string (in reverse order)
- * onto the stack, lets pop it off and put it into our decode
- * buffer... And when the decode buffer is full, write another
- * line...
- */
- while (sp > stack)
- {
- *bufptr++ = *(--sp);
- if (--bufcnt == 0)
- {
- if ((ret = out_line((char far *)buf, linewidth)) < 0)
- {
- free(buf);
- return(ret);
- }
- bufptr = buf;
- bufcnt = linewidth;
- }
- }
- }
- }
-
- ret = 0;
- if (bufcnt != linewidth)
- ret = out_line((char far *)buf, (linewidth - bufcnt));
-
- memfree(buf);
- memfree(prefix);
- memfree(suffix);
- memfree(stack);
-
- freewindow(giff_shape); // AJR - deinit draw stuff
- }
-
- return(ret);
- }
-
-
-
- // AJR - wrapper for unpack
- /* ---------------------- unpack_giff() ------------------- June 22,1994 */
- short unpack_giff(g_file_t *gf, unsigned short flags, short x, short y)
- {
- gifheader_t gh;
- imageblock_t iblk;
- fileinfo_t fi;
- long t;
- short b, c, done, i;
- unsigned char *p;
-
- // make sure gif file
- g_read_bytes((char far *)&gh, sizeof(gh) - 1, gf);
- if ( memcmp(gh.sig, "GIF", 3) )
- {
- pr2("NOT a gif file");
- return 1;
- }
-
- fi.width=gh.screenwidth;
- fi.depth=gh.screendepth;
- fi.bits=gh.flags & 0x07;
- fi.bits++;
- fi.background=gh.background;
-
- // get color map if there is one
- if ( gh.flags & 0x80 )
- {
- c=3 * (1<<((gh.flags & 7 ) + 1));
- g_read_bytes((char far *)fi.palette, c, gf);
- if ( flags & USE_PALETTE )
- {
- // AJR - vga only uses 6 bits
- for ( p=fi.palette, i=0; i < 768; i++, p++ )
- *p>>=2;
- setvgapalette(fi.palette);
- }
- }
-
- // step thru blocks
- done=0;
- while ( !done )
- {
- c=g_get_byte(gf);
-
- switch ( c )
- {
- case ',': // image block
- g_read_bytes((char far *)&iblk, sizeof(iblk) - 1, gf);
- fi.width=iblk.width;
- fi.depth=iblk.depth;
- //pr2("image block width %d height %d",fi.width, fi.depth);
-
- // local color map
- if ( iblk.flags & 0x80 )
- {
- //pr2("local color map");
- b= 3 * (1<<((iblk.flags & 0x07) + 1));
- g_read_bytes((char far *)&fi.palette, b, gf);
- fi.bits=iblk.flags & 0x07;
- fi.bits++;
- }
-
- fi.flags=iblk.flags;
-
- t=unpackgiffblock(fi.width, gf, x, y);
- //pr2("bad code count = %d", bad_code_count);
- if ( t )
- {
- //pr2("ERROR unpacking gif");
- done=1;
- //return 3;
- }
- break;
-
- case '!': // ext block
- //pr2("ext block");
- skip_ext(gf);
- break;
-
- case ';':
- case 0:
- // all done
- //pr2("all done c = %d %c", c, c);
- done=1;
- break;
-
- default:
- //pr2("unkhown block type %c %d", c,c);
- //return 2;
- done=1;
- break;
- }
-
- }
-
- return(0);
- }
-
-
-
- // AJR - ignores palette info in the picture, unless flags says otherwiae
- /* ---------------------- show_local_giff() ------------------ May 1,1994 */
- void show_local_giff(char *fname, short x, short y, unsigned short flags)
- {
- g_file_t *gf;
-
- // AJR - open and buffer file so we can do fast single byte reads
- gf=gopen_resource(fname, flags);
- if ( gf )
- {
- if ( unpack_giff(gf, flags, x, y) )
- {
- pr2("Error unpacking %s", fname);
- }
- }
- else
- {
- pr2("Error loading gif %s", fname);
- }
-
- }
-
-
- /* ------------------------------ EOF -------------------------------- */
-
- /* ---------------------------------------------------------------- */
- /* ---------------------------------------------------------------- */
- /* ---------------------------------------------------------------- */
- /* ------------ the include file ---------------------------------- */
- /* ---------------------------------------------------------------- */
- /* ---------------------------------------------------------------- */
- /* ---------------------------------------------------------------- */
-
- /*
-
- giff.h
-
- Copyright 1994, June 22 by Alec Russell, ALL rights reserved
-
- Created - 1994//6//22
-
- History:
- New file
-
- */
-
- #define OUT_OF_MEMORY -10
- #define BAD_CODE_SIZE -20
- #define READ_ERROR -1
- #define WRITE_ERROR -2
- #define OPEN_ERROR -3
- #define CREATE_ERROR -4
-
- typedef struct
- {
- char sig[6];
- unsigned int screenwidth, screendepth;
- unsigned char flags, background, aspect;
- }
- gifheader_t;
-
- typedef struct
- {
- unsigned int left, top, width, depth;
- unsigned char flags;
- }
- imageblock_t;
-
- typedef struct
- {
- int width, depth, bits;
- unsigned int flags;
- int background;
- unsigned char palette[768];
- }
- fileinfo_t;
-
- typedef struct
- {
- unsigned char blocksize;
- unsigned char flags;
- unsigned int delay;
- unsigned char transparent_color;
- unsigned char terminator;
- }
- controlblock_t;
-
- typedef struct
- {
- unsigned char blocksize;
- char applstring[8];
- unsigned char authentication[3];
- }
- application_t;
-
- typedef struct
- {
- unsigned char blocksize;
- unsigned int left, top, gridwith, gridheight;
- unsigned char cellwidth, cellheight;
- unsigned char forecolor, backcolor;
- }
- plaintext_t;
-
-
- /* ------------------------------ EOF -------------------------------- */
-
-
-
-